package cn.kwq.pcsystem.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Validator;
import cn.kwq.pcsystem.dto.*;
import cn.kwq.pcsystem.entity.*;
import cn.kwq.pcsystem.enums.RecoverEnum;
import cn.kwq.pcsystem.exception.CaptchaException;
import cn.kwq.pcsystem.exception.UserExistException;
import cn.kwq.pcsystem.exception.UserFormatException;
import cn.kwq.pcsystem.mapper.UserMapper;
import cn.kwq.pcsystem.pojo.JApplicationForm;
import cn.kwq.pcsystem.pojo.JRealUser;
import cn.kwq.pcsystem.pojo.JUser;
import cn.kwq.pcsystem.repository.ApplicationFormRepository;
import cn.kwq.pcsystem.repository.RealUserRepository;
import cn.kwq.pcsystem.repository.SquadDicRepository;
import cn.kwq.pcsystem.repository.UserRepository;
import cn.kwq.pcsystem.service.*;
import cn.kwq.pcsystem.utils.CaptchaUtils;
import cn.kwq.pcsystem.utils.CheckUtils;
import cn.kwq.pcsystem.utils.Md5Utils;
import cn.kwq.pcsystem.utils.WyuUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.text.ParseException;
import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author kwq
 * @since 2023-01-09
 */
@Service
@Slf4j
public class UserServiceImp extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    private ApplicationFormRepository applicationFormRepository;
    @Autowired
    private SquadDicRepository squadDicRepository;

    @Autowired
    UserMapper userMapper;
    @Autowired
    UserRepository userRepository;

    @Autowired
    RealUserRepository realUserRepository;

    @Autowired
    DepartmentDicService departmentDicService;

    @Autowired
    MajorDicService majorDicService;

    @Autowired
    SquadDicService squadDicService;

    @Autowired
    ApplicationFormService applicationFormService;

    /**
     * 注册
     * @param dto
     * @return
     * @throws UserExistException
     * @throws UserFormatException
     * @throws CaptchaException
     */
    @Override
    @Transactional(rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)//读已提交
    public boolean register(RegisterDTO dto) throws UserExistException, UserFormatException, CaptchaException {
        //验证码校验
        if(!CaptchaUtils.checkCaptcha(dto.getCaptcha(), dto.getUuid())){
            throw new CaptchaException();
        }
        //幂等性验证
        QueryWrapper<User> wrapper=new QueryWrapper<>();
        wrapper.eq("age",dto.getAge()).
                eq("email",dto.getEmail())
                .eq("phone",dto.getPhone())
                .eq("user_name",dto.getUserName())
                .eq("sex",dto.getSex());
        User user = userMapper.selectOne(wrapper);
        if (user == null){

            CheckUtils.doCheck()//开启链式工具类
                    .checkFormat(dto.getPhone(),dto.getEmail())//检查用户邮箱和手机格式是否正确
                    .checkExist(dto.getUserName(),dto.getPhone(),dto.getEmail());//检查该用户的手机号 邮箱 名字是否已注册
            //设置其他信息
            //MD5加密
            Md5DTO md5Dto = Md5Utils.Md5Encrypt(dto.getPassword());
            //因为鉴权框架缘故，只能用mp更新插入
            int insert = userMapper.insert(
                    User.builder()
                            .age(dto.getAge())
                            .sex(dto.getSex())
                            .email(dto.getEmail())
                            .password(md5Dto.encrypt())
                            .salt(md5Dto.salt())
                            .phone(dto.getPhone())
                            .userName(dto.getUserName())
                            .createdDate(new Date())//创建时间
                            .updateDate(new Date())//更新时间
                            .deleted(0)
                            .build());
            return insert!=0;
        }
        //重复注册情况(幂等性校验)
        return false;
    }

    /**
     * 登录
     * @param dto
     * @return
     * @throws UserExistException
     */
    @Override
    @Transactional(rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)//读已提交
    public Long login(LoginDTO dto) throws UserExistException {
        User user;
        QueryWrapper<User> wrapper=new QueryWrapper<>();
        //鉴别登录类型
       if (Validator.isEmail(dto.getLoginKey())){
           log.info("邮箱登录["+dto.getLoginKey()+"]");
           wrapper.eq("email",dto.getLoginKey());
       } else if (Validator.isMobile(dto.getLoginKey())) {
           log.info("手机号登录["+dto.getLoginKey()+"]");
           wrapper.eq("phone",dto.getLoginKey());
       }else {
           log.info("姓名登录["+dto.getLoginKey()+"]");
           wrapper.eq("user_name",dto.getLoginKey());
       }
        user = userMapper.selectOne(wrapper);
        //用户不存在
        if (user==null){
            log.error("用户不存在["+dto.getLoginKey()+"]");
            throw new UserExistException("用户不存在");
        }
        //校验密码正确与否
        if(!Md5Utils.Md5Validate(user.getPassword(),dto.getPassword(),user.getSalt())){
            log.error("密码错误["+dto.getLoginKey()+"]");
            throw new RuntimeException("密码错误");
        }
        return user.getUserId();
    }

    /**
     * 不通过注册生成用户
     * @param dto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)//读已提交
    public User createUserNotThroughRegister(RealUserDTO dto) {
        User user;
        QueryWrapper<User> wrapper=new QueryWrapper<>();
        user = userMapper.selectOne(wrapper);
        if (user!=null){
            return null;
        }
        Md5DTO md5DTO = Md5Utils.Md5Encrypt("123456");
        JUser save = userRepository.save(JUser
                .builder()
                .userName(dto.getUserName())
                .password(md5DTO.encrypt())
                .salt(md5DTO.salt())
                .deleted(0)
                .build());
        Long userId = save.getUserId();
        //存入真实信息
        realUserRepository.save(JRealUser
                .builder()
                .userId(userId)
                .realUserName(dto.getRealUserName())
                .departmentId(dto.getDepartmentId())
                .deleted(0)
                .schoolNum(dto.getSchoolNum())
                .isStudent(dto.getIsStudent())
                .remark(dto.getRemark())
                .build());
        return BeanUtil.copyProperties(save,User.class);
    }

    /**
     * 更新用户信息
     * @param oldPsw
     * @param newPsw
     * @param userId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)//读已提交
    public boolean updateUserPsw(String oldPsw , String newPsw , Long userId ) {
        QueryWrapper<User> wrapper=new QueryWrapper<>();
        wrapper.eq("user_id",userId);
        User user = userMapper.selectOne(wrapper);
        if (user==null){
            throw new RuntimeException("用户不存在或已被停用");
        }
        //校验密码正确与否
        if(!Md5Utils.Md5Validate(user.getPassword(),oldPsw,user.getSalt())){
            log.error("密码错误["+userId+" 修改密码"+"]");
            throw new RuntimeException("密码错误");
        }
        //MD5加密
        Md5DTO md5Dto = Md5Utils.Md5Encrypt(newPsw);
        //执行更新
        user.setPassword(md5Dto.encrypt())
                .setSalt(md5Dto.salt());
        JUser save = userRepository.save(BeanUtil.copyProperties(user, JUser.class));
        return Md5Utils.Md5Validate(save.getPassword(),newPsw,save.getSalt());
    }

    /**
     * 停用或启用用户
     */
    @Override
    @Transactional(rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)//读已提交
    public void inactivationOrRecoverUser(RecoverEnum recoverEnum , Long userId) {
        //获取停用还是恢复
        int type = recoverEnum.getType();
        User user = userMapper.selectById(userId);
        user.setDeleted(type);
        JUser jUser = BeanUtil.copyProperties(user, JUser.class);
        userRepository.save(jUser);
    }

    @Override
    @Transactional(rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)//读已提交
    public Map<String,String> loginWyuSys(String token) throws IOException, ParseException {
        //拿取token进行wyu官网爬取信息
        WyuDTO wyuDTO = WyuUtils.getInstance().getMessageByWyuSys(token);
        JApplicationForm from=new JApplicationForm();

        Map<String,String> map=new HashMap<>();
        //获取学号，检验幂等性
        String stuNum = wyuDTO.stuNum();
        //幂等性校验
        QueryWrapper<ApplicationForm> wrapper=new QueryWrapper<>();
        ApplicationForm one = applicationFormService.getOne(wrapper);
        if (one!=null){
            log.warn("触发幂等性校验time：{},name:{},stuNum:{}", DateUtil.now(),one.getName(),stuNum);
            map.put("applicationFromId",one.getApplicationFromId().toString());
            map.put("autoGetIdSuccess",checkAutoGetId(one));
            return map;
        }

        from.setFacultyName(wyuDTO.institute())
                .setMajorName(wyuDTO.major())
                .setWyuClass(wyuDTO.wyuClass())
                .setGrade(wyuDTO.grade())
                .setStuNum(stuNum);

        QueryWrapper<DepartmentDic> departmentDic=new QueryWrapper<>();
        departmentDic.eq("department_name",wyuDTO.institute());
        QueryWrapper<MajorDic> majorDic=new QueryWrapper<>();
        majorDic.eq("major_name",wyuDTO.major());
        QueryWrapper<SquadDic> squadDic=new QueryWrapper<>();
        squadDic.eq("squad_name",wyuDTO.wyuClass());

        DepartmentDic department = departmentDicService.getOne(departmentDic);
        MajorDic major = majorDicService.getOne(majorDic);
        SquadDic squad = squadDicService.getOne(squadDic);

        //自动查找专业代码
        Optional.ofNullable(department).ifPresent(item->from.setDepartmentId(item.getDepartmentId()));
        Optional.ofNullable(major).ifPresent(item->from.setMajorId(item.getMajorId()));
        Optional.ofNullable(squad).ifPresent(item->from.setSquadId(item.getSquadId()));

        JApplicationForm save = applicationFormRepository.save(from);

        map.put("applicationFromId",save.getApplicationFromId().toString());
        map.put("autoGetIdSuccess",checkAutoGetId(save));

        return map;
    }

    private String checkAutoGetId(ApplicationForm one) {
        if(one.getDepartmentId()==null){
            return "false";
        } else if (one.getMajorId()==null) {
            return "false";
        }else if (one.getSquadId()==null){
            return "false";
        }
        return "true";

    }
    private String checkAutoGetId(JApplicationForm one) {
        if(one.getDepartmentId()==null){
            return "false";
        } else if (one.getMajorId()==null) {
            return "false";
        }else if (one.getSquadId()==null){
            return "false";
        }
        return "true";

    }


}
